/*
 * This application was developed using and referring to the sample code provided by Sun Microsystems
 * Hence I am including this mandatory copyright notice in order to comply with the rules

 * Copyright (c) 2007 Sun Microsystems, Inc.
 * Copyright (c) 2010 Oracle
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */

package ritcs.sensors.app;

import com.sun.spot.resources.transducers.SwitchEvent;
import ritcs.sensors.util.LocateService;
import ritcs.sensors.util.LocateServiceListener;
import ritcs.sensors.util.PacketHandler;
import ritcs.sensors.util.PacketReceiver;
import ritcs.sensors.util.PacketTransmitter;
import ritcs.sensors.util.PeriodicTask;

import com.sun.spot.peripheral.Spot;
import com.sun.spot.resources.Resources;
import com.sun.spot.resources.transducers.ITriColorLED;
import com.sun.spot.resources.transducers.ITriColorLEDArray;
import com.sun.spot.resources.transducers.LEDColor;
import com.sun.spot.util.IEEEAddress;
import com.sun.spot.util.Utils;
import com.sun.spot.io.j2me.radiogram.Radiogram;
import com.sun.spot.io.j2me.radiogram.RadiogramConnection;
import com.sun.spot.resources.transducers.ILightSensor;
import com.sun.spot.resources.transducers.ISwitch;
import com.sun.spot.resources.transducers.ISwitchListener;
import com.sun.spot.service.BootloaderListenerService;
import com.sun.spot.resources.transducers.ITemperatureInput;
import java.io.IOException;
import javax.microedition.io.Connector;

import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
/**
 * This is the SPOT version of the sensors which streams the light and temperature readings
 * @author chimbs
 */
public class SPOTApp extends MIDlet
        implements LocateServiceListener, PacketHandler, PacketTypes , ISwitchListener {
    
    private static final long SERVICE_CHECK_INTERVAL = 10000;   // = 10 seconds
    
    private static final String VERSION = "2.0";
    
    private boolean connected = false;
    private String messages[] = new String[50];
    private int stringIndex = 0;
    private Temperature te; 
    private LocateService servLoc;
    private PacketReceiver receiver;
    private PacketTransmitter transmitter;
    private RadiogramConnection hostConnection;
    
    private ITriColorLEDArray leds = (ITriColorLEDArray) Resources.lookup(ITriColorLEDArray.class);
    private ITriColorLED led1 = leds.getLED(0);
    private ITriColorLED led2 = leds.getLED(1);
    
  
   
    public void initialize() { 
        led1.setRGB(50,0,0);     // Red = not active
        led1.setOn();

        // create a service locator using the correct port & connection commands
        servLoc = new LocateService(this, BROADCAST_PORT, SERVICE_CHECK_INTERVAL,
                                    LOCATE_DISPLAY_SERVER_REQ,
                                    DISPLAY_SERVER_AVAIL_REPLY,
                                    4);
        servLoc.setStatusLed(led2);
        
        // create the monitor to handle accelerometer commands

        stringIndex = 0;

    }
    
  
    public void run() {
        
        led1.setRGB(50,0,0);     // Red = not active
        led1.setOn();

        servLoc.start();            // look for host app and call serviceLocated() when found
        while (true) {
            Utils.sleep(30000);     // real work is done elsewhere; allow deep sleep while trying to connect
        }
    }

    /**
     * Called after the service is located. A part of the interface LocateServiceListener
     * Used to register handlers
     * @param serviceAddress
     */
    
    public void serviceLocated(long serviceAddress) {
        try {
            if (hostConnection != null) {
                hostConnection.close();
            }
            hostConnection = (RadiogramConnection)Connector.open("radiogram://" +
                            IEEEAddress.toDottedHex(serviceAddress) + ":" + CONNECTED_PORT);
            hostConnection.setTimeout(-1);    // no timeout
        } catch (IOException ex) {
            System.out.println("Failed to open connection to host: " + ex);
            closeConnection();
            return;
        }
        connected = true;
        led1.setRGB(0, 30, 0);           // Green = connected

        transmitter = new PacketTransmitter(hostConnection);     // set up thread to transmit replies to host
        transmitter.setServiceName("Spot Transmitter");
        transmitter.start();
        
        receiver = new PacketReceiver(hostConnection);        // set up thread to receive & dispatch commands
        receiver.setServiceName("Spot Server");
        
        receiver.registerHandler(this, DISPLAY_SERVER_RESTART);     // specify commands this class handles
        receiver.registerHandler(this, DISPLAY_SERVER_QUITTING);
        receiver.registerHandler(this, PING_REQ);
        receiver.registerHandler(this, BLINK_LEDS_REQ);
        receiver.registerHandler(this, GET_TEMPERATURE);
        receiver.registerHandler(this, SET_PERIOD);
        receiver.registerHandler(this, GET_GUI_INFO);
        receiver.start();

    }

    /**
     * Called to declare that the connection to the host is no longer present.
     */
    public void closeConnection() {
        if (!connected) {
            return;
        }
        led1.setRGB(50,0,0);     // Red = not active
        connected = false;
        transmitter.stop();
        receiver.stop();
        Utils.sleep(100);       // give things time to shut down
        try {
            if (hostConnection != null) {
                hostConnection.close();
                hostConnection = null;
            }
        } catch (IOException ex) {
            System.out.println("Failed to close connection to host: " + ex);
        }
        servLoc.start();
    }
    
/**
 * Handle a packet once it arrives based on the type
 * @param type
 * @param pkt
 */
    public void handlePacket(byte type, Radiogram pkt) {
        try {
            switch (type) {
                case DISPLAY_SERVER_RESTART:
                case DISPLAY_SERVER_QUITTING:
                    closeConnection();              // we need to reconnect to host
                    break;
                case PING_REQ:
                    led2.setRGB(40, 0, 0);       // Red = ping
                    led2.setOn();
                    sendPingReply(pkt);
                    led2.setOff();
                    break;
                case BLINK_LEDS_REQ:
                    blinkLEDs();
                    if (connected) {
                        led1.setRGB(0, 30, 0);   // Green = connected
                    } else {
                        led1.setRGB(50, 0, 0);   // Red = not active
                    }
                    led1.setOn();
                    break;
                case GET_TEMPERATURE:
                    Radiogram rdg = transmitter.newDataPacket(GET_TEMPERATURE_REPLY);
                    ITemperatureInput temp =(ITemperatureInput) Resources.lookup(ITemperatureInput.class);

                    ILightSensor light = (ILightSensor)Resources.lookup(ILightSensor.class);

                    rdg.writeDouble(temp.getCelsius());
                    rdg.writeLong(System.currentTimeMillis());
                    int network =  pkt.readInt();
                    rdg.writeInt(network);
                    rdg.writeDouble(light.getAverageValue());
                    transmitter.send(rdg);
                   
                     te = new Temperature(transmitter,network);
                    te.start();
                    break;

                case GET_GUI_INFO:
                    Radiogram rdg3 = transmitter.newDataPacket(GET_GUI_INFO_REPLY);

                   //int batt = Spot.getInstance().getPowerController().getVbatt();

                  int batt =  Spot.getInstance().getPowerController().getVbatt();
                    rdg3.writeInt(batt);
                   rdg3.writeInt(pkt.getLinkQuality());
                   transmitter.send(rdg3);

                   ITriColorLED r = leds.getLED(6);
                   r.setRGB(0, 0, 70);
                   r.setOn();


                    break;
                case SET_PERIOD: 
                    int period = pkt.readInt();
                    te.setTaskPeriod(period);
                    break;
                    
            }
        } catch (IOException ex) {
            closeConnection();
        }
    }
    
    // handle Ping command
    private void sendPingReply(Radiogram pkt) throws IOException {
        int linkQuality = pkt.getLinkQuality();
        int corr = pkt.getCorr();
        int rssi = pkt.getRssi();
        int battery = Spot.getInstance().getPowerController().getVbatt();
        Radiogram rdg = transmitter.newDataPacket(PING_REPLY);
        rdg.writeInt(linkQuality);          // report how well we can hear server
        rdg.writeInt(corr);
        rdg.writeInt(rssi);
        rdg.writeInt(battery);
        transmitter.send(rdg);
        for (int im = 0; im < stringIndex; im++) {
            Utils.sleep(30);                // give host time to process packet
            rdg = transmitter.newDataPacket(MESSAGE_REPLY);
            rdg.writeUTF(messages[im]);
            transmitter.send(rdg);
            // System.out.println("Sent message: " + messages[im]);
        }
        stringIndex = 0;
        Utils.sleep(200);
    }
    
    // handle Blink LEDs command
    private void blinkLEDs() {
        for (int i = 0; i < 10; i++) {          // blink LEDs 10 times = 10 seconds
            leds.setColor(LEDColor.MAGENTA);
            leds.setOn();
            Utils.sleep(250);
            leds.setOff();
            Utils.sleep(750);
        }
    }

    /**
     * Add a message to the queue to be sent to the host at a later time. 
     * Messages will be sent after the next Ping request arrives.
     *
     * @param msg the String to be sent
     */
    public void queueMessage (String msg) {
        if (stringIndex < messages.length) {
            messages[stringIndex++] = (msg.length() < Radiogram.MAX_LENGTH) ? msg : msg.substring(0, Radiogram.MAX_LENGTH - 1);
            // System.out.println("Queuing message: " + msg);
        } else {
            // System.out.println("No room in queue for message: " + msg);
        }
    }


    //////////////////////////////////////////////////////////
    //
    // Standard MIDlet class methods
    //
    //////////////////////////////////////////////////////////

    /**
     * MIDlet call to start our application.
     */
    protected void startApp() throws MIDletStateChangeException {
        BootloaderListenerService.getInstance().start();       // Listen for downloads/commands over USB connection
        ISwitch sw1 = (ISwitch) Resources.lookup(ISwitch.class, "sw1");
        sw1.addISwitchListener(this);
        initialize();
        run();
    }

    /**
     * This will never be called by the Squawk VM.
     */
    protected void pauseApp() {
        // This will never be called by the Squawk VM
    }

    /**
     * Called if the MIDlet is terminated by the system.
     * @param unconditional If true the MIDlet must cleanup and release all resources.
     */
    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {
    }

    public void switchPressed(SwitchEvent evt) {
    }

    public void switchReleased(SwitchEvent evt) {
        closeConnection();
    }

}
